"""
@author: 江同学呀
@file:  delete_by_query_api.py
@date: 2025/3/21 20:47
@desc: 
    https://www.elastic.co/guide/en/elasticsearch/reference/7.17/docs-delete-by-query.html
"""
from typing import Union, Type, Dict, Iterable, Literal, Optional, List, Any

from elastic_transport import ObjectApiResponse

from espc.common.common import Number
from espc.core.core import core
from espc.orm.api.base_api import BaseApi
from espc.orm.model.dsl.paginate_search_results import Slice
from espc.orm.model.dsl.queries.base_queries import _BaseQueries
from espc.orm.model.dsl.search_fields import Source
from espc.orm.model.mapping.field.base_field.base_field import _BaseField
from espc.orm.model.model.base_alias import BaseAlias
from espc.orm.model.model.base_index import BaseIndex
from espc.orm.model.scripting.script import Script
from espc.orm.model.text_analysis.analyzer import BaseAnalyzer


class DeleteByQueryApi(BaseApi):
    """
    按查询删除
    """

    def __init__(
            self, index: Union[str, Type[BaseIndex], Type[BaseAlias], List[Type[BaseIndex]], List[Type[BaseAlias]]],
            *args, **kwargs
    ):
        super().__init__(*args, **kwargs)
        self._body: Dict[str, Union[str, List, Dict, Number]] = {}

        self._index: Union[str, Type[BaseIndex], Type[BaseAlias], List[Type[BaseIndex]], List[Type[BaseAlias]]] = index

        self._query: Optional[List[_BaseQueries]] = None
        self._script: Union[str, Dict, Script, None] = None
        self._doc: Optional[Dict[Union[str, _BaseField], Any]] = None
        self._max_docs: Optional[int] = None
        self._source: Union[Iterable[Union[_BaseField, str]], Source, Dict, bool, None] = None
        self._slice: Union[str, Dict, Slice, None] = None

        self._source: Union[Iterable[Union[_BaseField, str]], Source, Dict, bool, None] = None
        return

    def query(self, *query: _BaseQueries) -> "DeleteByQueryApi":
        """
        添加查询条件
        :param query:
        :return:
        """
        if self._query is None:
            self._query: List[_BaseQueries] = []
        self._query.extend(query)
        return self

    def max_docs(self, max_docs: int) -> "DeleteByQueryApi":
        """
        设置最大文档数
        :param max_docs:
        :return:
        """
        self._max_docs: int = max_docs
        return self

    def source(
            self, fields: Union[_BaseField, str, Iterable[Union[_BaseField, str]], Source, Dict, bool],
            *args: Union[_BaseField, str]
    ) -> "DeleteByQueryApi":
        """
        设置返回字段
        :param fields:
        :return:
        """
        if not args:
            self._source: Union[Iterable[Union[_BaseField, str]], Source, Dict, bool] = fields
        elif isinstance(fields, Iterable):
            self._source: Union[Iterable[Union[_BaseField, str]], Source, Dict, bool] = list(fields) + list(args)
        elif isinstance(fields, (_BaseField, str)):
            self._source: Union[Iterable[Union[_BaseField, str]], Source, Dict, bool] = [fields] + list(args)
        else:
            self._source: Union[Iterable[Union[_BaseField, str]], Source, Dict, bool] = fields
        return self

    def slice(self, slice_: Union[str, Dict, Slice]) -> "DeleteByQueryApi":
        """
        设置分片
        :param slice_:
        :return:
        """
        self._slice: Union[str, Dict, Slice] = slice_
        return self

    def _build(self) -> Dict:
        _body: Dict = {}
        if self._query:
            _body["query"] = {query.type: query._build() for query in self._query}
        if self._max_docs is not None:
            _body["max_docs"] = self._max_docs
        if self._source:
            _body["_source"] = (
                self._source._build()
                if isinstance(self._source, Source)
                else (
                    self._source
                    if isinstance(self._source, (Dict, bool)) else (
                        [s if isinstance(s, str) else s._field_name for s in self._source]
                        if isinstance(self._source, Iterable) else self._source
                    )
                )
            )
        if self._slice:
            _body["slice"] = self._slice._build() if isinstance(self._slice, Slice) else self._slice
        return _body

    def post(
            self, human: bool = None, filter_path: Union[str, Iterable[str]] = None,
            # flat_settings: bool = None,
            error_trace: bool = None, allow_no_indices: bool = None, analyzer: Union[str, BaseAnalyzer] = None,
            analyze_wildcard: bool = None, conflicts: Literal["abort", "proceed"] = None, default_operator: str = None,
            df: str = None, expand_wildcards: Literal["all", "open", "closed", "hidden", "none"] = None,
            from_: int = None, ignore_unavailable: bool = None, lenient: bool = None, max_docs: int = None,
            preference: str = None, q: str = None, request_cache: bool = None, refresh: bool = None,
            requests_per_second: int = None, routing: str = None, scroll: str = None, scroll_size: int = None,
            search_type: Literal["query_then_fetch", "dfs_query_then_fetch"] = None,
            search_timeout: str = None, slices: int = None,
            sort: Union[str, Iterable[str], _BaseField, Iterable[_BaseField]] = None, stats: str = None,
            terminate_after: int = None, timeout: str = None, version: bool = None, wait_for_active_shards: str = None,
            *args, **kwargs
    ) -> Dict:
        """
        POST请求
        :param human:
            Statistics are returned in a format suitable for humans (e.g. "exists_time": "1h" or "size": "1kb") and for
            computers (e.g. "exists_time_in_millis": 3600000 or "size_in_bytes": 1024). The human readable values can be
            turned off by adding ?human=false to the query string. This makes sense when the stats results are being
            consumed by a monitoring tool, rather than intended for human consumption. The default for the human flag is
            false.
            统计数据以适合人类的格式返回（例如“exists_time”： “1h”或“size”： “1kb”）和计算机（例如“exists_time_in_millis”： 3600000
            或 “size_in_bytes”： 1024）。可以通过将 ？human=false 添加到查询字符串来关闭人类可读值。当统计结果由监控工具使用而不是供人类
            使用时，这是有意义的。人为标志的默认值为 false。
        :param filter_path:
            All REST APIs accept a filter_path parameter that can be used to reduce the response returned by
            Elasticsearch. This parameter takes a comma separated list of filters expressed with the dot notation.
            所有 REST API 都接受 filter_path 参数，该参数可用于减少 Elasticsearch 返回的响应。此参数采用逗号分隔的过滤器列表，以点表示
            法表示
        # :param flat_settings:
        #     The flat_settings flag affects rendering of the lists of settings. When the flat_settings flag is true,
        #     settings are returned in a flat format.
        #     flat_settings 标志会影响设置列表的呈现。当 flat_settings 标志为 true 时，设置以平面格式返回
        :param error_trace:
            By default when a request returns an error Elasticsearch doesn’t include the stack trace of the error. You
            can enable that behavior by setting the error_trace url parameter to true.
            默认情况下，当请求返回错误时，Elasticsearch 不包含错误的堆栈跟踪。您可以通过将 error_trace url 参数设置为 true 来启用该行为。
        :param allow_no_indices:
            (Optional, Boolean) If false, the request returns an error if any wildcard expression, index alias, or _all
            value targets only missing or closed indices. This behavior applies even if the request targets other open
            indices. For example, a request targeting foo*,bar* returns an error if an index starts with foo but no
            index starts with bar.
            Defaults to true.

            （可选，布尔值）如果为 false，则当任何通配符表达式、索引别名或_all值仅针对缺失或已关闭的索引时，请求将返回错误。即使请求以其他开放
            索引为目标，此行为也适用。例如，如果索引以 foo 开头，但没有索引以 bar 开头，则以 foo*，bar* 为目标的请求将返回错误。
            默认为 true。
        :param analyzer:
            (Optional, string) Analyzer to use for the query string.
            This parameter can only be used when the q query string parameter is specified.

            （可选，字符串）用于查询字符串的分析器。
            仅当指定了 q query string 参数时，才能使用此参数。
        :param analyze_wildcard:
            (Optional, Boolean) If true, wildcard and prefix queries are analyzed. Defaults to false.
            This parameter can only be used when the q query string parameter is specified.

            （可选，布尔值）如果为 true，则分析通配符和前缀查询。默认为 false。
            仅当指定了 q query string 参数时，才能使用此参数。
        :param conflicts:
            (Optional, string) What to do if update by query hits version conflicts: abort or proceed. Defaults to abort.
            （可选，字符串）如果按查询更新命中版本冲突，该怎么办：abort 或 proceed。默认为 abort。
        :param default_operator:
            (Optional, string) The default operator for query string query: AND or OR. Defaults to OR.
            This parameter can only be used when the q query string parameter is specified.

            （可选，字符串）查询字符串查询的默认运算符：AND 或 OR。默认为 OR。
            仅当指定了 q query string 参数时，才能使用此参数。
        :param df:
            (Optional, string) Field to use as default where no field prefix is given in the query string.
            This parameter can only be used when the q query string parameter is specified.

            （可选，字符串）用作默认字段的字段，其中查询字符串中未提供字段前缀。
            仅当指定了 q query string 参数时，才能使用此参数。
        :param expand_wildcards:
            (Optional, string) Type of index that wildcard patterns can match. If the request can target data streams,
            this argument determines whether wildcard expressions match hidden data streams. Supports comma-separated
            values, such as open,hidden. Valid values are:
                - all
                    Match any data stream or index, including hidden ones.
                - open
                    Match open, non-hidden indices. Also matches any non-hidden data stream.
                - closed
                    Match closed, non-hidden indices. Also matches any non-hidden data stream. Data streams cannot be
                    closed.
                - hidden
                    Match hidden data streams and hidden indices. Must be combined with open, closed, or both.
                - none
                    Wildcard patterns are not accepted.
            Defaults to open.

            （可选，字符串）通配符模式可以匹配的索引类型。如果请求可以定位数据流，则此参数将确定通配符表达式是否与隐藏的数据流匹配。支持逗号分隔
            值，例如 open，hidden。有效值为：
                - all
                    匹配任何数据流或索引，包括隐藏的数据流或索引。
                - open
                    匹配打开的非隐藏索引。还匹配任何非隐藏的数据流。
                - closed
                    匹配已关闭的非隐藏索引。还匹配任何非隐藏的数据流。无法关闭数据流。
                - hidden
                    匹配隐藏的数据流和隐藏的索引。必须与 open 和/或 closed 组合使用。
                - none
                    不接受通配符模式。
            默认为 open。
        :param from_:
            (Optional, integer) Starting document offset. Defaults to 0.
            （可选，整数）起始文档偏移。默认值为 0。
        :param ignore_unavailable:
            (Optional, Boolean) If false, the request returns an error if it targets a missing or closed index.
            Defaults to false.
            （可选，布尔值）如果为 false，则当请求以缺失或已关闭的索引为目标时，请求将返回错误。默认为 false。
        :param lenient:
            (Optional, Boolean) If true, format-based query failures (such as providing text to a numeric field) in the
            query string will be ignored. Defaults to false.
            This parameter can only be used when the q query string parameter is specified.

            （可选，布尔值）如果为 true，则将忽略查询字符串中基于格式的查询失败（例如向数字字段提供文本）。默认为 false。
            仅当指定了 q query string 参数时，才能使用此参数。
        :param max_docs:
            (Optional, integer) Maximum number of documents to process. Defaults to all documents.
            （可选，整数）要处理的最大文档数。默认为所有文档。
        :param preference:
            (Optional, string) Specifies the node or shard the operation should be performed on. Random by default.
            （可选，字符串）指定应对其执行作的节点或分片。默认情况下为 Random。
        :param q:
            (Optional, string) Query in the Lucene query string syntax.
            （可选，字符串）查询。
        :param request_cache:
            (Optional, Boolean) If true, the request cache is used for this request. Defaults to the index-level setting.
            （可选，布尔值）如果为 true，则请求缓存用于此请求。默认为索引级别设置。
        :param refresh:
            (Optional, Boolean) If true, Elasticsearch refreshes affected shards to make the operation visible to
            search. Defaults to false.
            （可选，布尔值）如果为 true，则 Elasticsearch 会刷新受影响的分片，以使作对搜索可见。默认为 false。
        :param requests_per_second:
            (Optional, integer) The throttle for this request in sub-requests per second. Defaults to -1 (no throttle).
            （可选，整数）此请求的节流（以每秒子请求数为单位）。默认为 -1（无节流）。
        :param routing:
            (Optional, string) Custom value used to route operations to a specific shard.
            （可选，字符串）用于将作路由到特定分片的自定义值。
        :param scroll:
            (Optional, time value) Period to retain the search context for scrolling. See Scroll search results.
            （可选，时间值）Period 保留用于滚动的搜索上下文。请参阅滚动搜索结果。
        :param scroll_size:
            (Optional, integer) Size of the scroll request that powers the operation. Defaults to 1000.
            （可选，整数）为作提供支持的 scroll 请求的大小。默认值为 1000。
        :param search_type:
            (Optional, string) The type of the search operation. Available options:
                - query_then_fetch
                - dfs_query_then_fetch

            （可选，字符串）搜索作的类型。可用选项：
                - query_then_fetch
                - dfs_query_then_fetch
        :param search_timeout:
            (Optional, time units) Explicit timeout for each search request. Defaults to no timeout.
            （可选，时间单位）每个搜索请求的显式超时。默认为无超时。
        :param slices:
            (Optional, integer) The number of slices this task should be divided into. Defaults to 1 meaning the task
            isn’t sliced into subtasks.
            （可选，整数）此任务应划分为的切片数。默认为 1，表示任务未被切分成子任务。
        :param sort:
            (Optional, string) A comma-separated list of <field>:<direction> pairs.
            （可选，字符串）以逗号分隔的 ： 对列表。
        :param stats:
            (Optional, string) Specific tag of the request for logging and statistical purposes.
            （可选，字符串）用于日志记录和统计目的的请求的特定标记。
        :param terminate_after:
            (Optional, integer) Maximum number of documents to collect for each shard. If a query reaches this limit,
            Elasticsearch terminates the query early. Elasticsearch collects documents before sorting.
            （可选，整数）要为每个分片收集的最大文档数。如果查询达到此限制，Elasticsearch 会提前终止查询。Elasticsearch 在排序之前收集文档。
        :param timeout:
            (Optional, time units) Period each update request waits for the following operations:
                - Dynamic mapping updates
                - Waiting for active shards
            Defaults to 1m (one minute). This guarantees Elasticsearch waits for at least the timeout before failing.
            The actual wait time could be longer, particularly when multiple waits occur.

            （可选，时间单位）周期每个更新请求等待以下作：
                - 动态映射更新
                - 等待活动分片
            默认为 1m（1 分钟）。这保证了 Elasticsearch 在失败之前至少等待超时。实际等待时间可能会更长，尤其是在发生多个等待时。
        :param version:
            (Optional, Boolean) If true, returns the document version as part of a hit.
            （可选，布尔值）如果为 true，则返回文档版本作为点击的一部分。
        :param wait_for_active_shards:
            (Optional, string) The number of shard copies that must be active before proceeding with the operation. Set
            to all or any positive integer up to the total number of shards in the index (number_of_replicas+1).
            Default: 1, the primary shard.
            （可选，字符串）在继续执行作之前必须处于活动状态的分片副本数。设置为全部或任何正整数，最大为索引中的分片总数
            （number_of_replicas+1）。默认值：1，主分片。
        :param args:
        :param kwargs:
        :return:
        """
        body = self._build()
        resp: ObjectApiResponse = core.es.delete_by_query(
            index=(
                self._index
                if isinstance(self._index, str)
                else (
                    [_i if isinstance(_i, str) else _i._name for _i in self._index]
                    if isinstance(self._index, Iterable)
                    else self._index._name
                )
            ),
            body=body,

            human=human,
            filter_path=filter_path,
            # flat_settings=flat_settings,
            error_trace=error_trace,
            allow_no_indices=allow_no_indices,
            analyzer=analyzer,
            analyze_wildcard=analyze_wildcard,
            conflicts=conflicts,
            default_operator=default_operator,
            df=df,
            expand_wildcards=expand_wildcards,
            from_=from_,
            ignore_unavailable=ignore_unavailable,
            lenient=lenient,
            max_docs=max_docs,
            preference=preference,
            q=q,
            request_cache=request_cache,
            refresh=refresh,
            routing=routing,
            scroll=scroll,
            scroll_size=scroll_size,
            search_type=search_type,
            search_timeout=search_timeout,
            sort=sort,
            stats=stats,
            terminate_after=terminate_after,
            timeout=timeout,
            version=version,
            wait_for_active_shards=wait_for_active_shards,
            *args, **kwargs
        )
        return resp.body


